package com.db.williamchart.view;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.OneShotPreDrawListener;

import com.db.williamchart.ChartContract;
import com.db.williamchart.R;
import com.db.williamchart.animation.ChartAnimation;
import com.db.williamchart.animation.DefaultDonutAnimation;
import com.db.williamchart.animation.DonutNoAnimation;
import com.db.williamchart.data.DonutDataPoint;
import com.db.williamchart.data.Frame;
import com.db.williamchart.data.Paddings;
import com.db.williamchart.data.configuration.DonutChartConfiguration;
import com.db.williamchart.renderer.DonutChartRenderer;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class DonutChartView extends FrameLayout implements ChartContract.DonutView {
    private final float defaultThickness = 50f;
    private final int defaultColor = Color.BLACK;
    private final int defaultBackgroundColor = Color.TRANSPARENT;
    private final float defaultStartAngle = 90f;
    private final float defaultDonutTotal = 100f;
    private final List<Float> editModeSampleData = Collections.singletonList(70f);

    private float donutThickness = defaultThickness;

    private List<Integer> donutColors = Collections.singletonList(defaultColor);

    private int donutBackgroundColor = defaultBackgroundColor;

    private boolean donutRoundCorners = false;

    private float donutTotal = defaultDonutTotal;

    private ChartAnimation<DonutDataPoint> animation = new DefaultDonutAnimation();

    private Canvas canvas;

    private ChartContract.DonutRenderer renderer = new DonutChartRenderer(this, new DonutNoAnimation());

    private Paint paint = new Paint();

    private DonutChartConfiguration getDonutChartConfiguration() {
        return new DonutChartConfiguration(
                getMeasuredWidth(),
                getMeasuredHeight(),
                new Paddings(
                        getPaddingLeft(),
                        getPaddingTop(),
                        getPaddingRight(),
                        getPaddingBottom()
                ),
                donutThickness,
                donutTotal,
                donutColors.size(),
                donutBackgroundColor
        );
    }

    public DonutChartView(@NonNull Context context) {
        this(context,null);
    }

    public DonutChartView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DonutChartView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    @SuppressLint("CustomViewStyleable")
    private void init(@NonNull Context context, @Nullable AttributeSet attrs) {
        setBackgroundColor(Color.TRANSPARENT);
        handleAttributes(context.obtainStyledAttributes(attrs, R.styleable.DonutChartAttrs));
        handleEditMode();
    }

    @Override
    public void drawArc(List<Float> degrees, Frame innerFrame) {
        if (donutRoundCorners)
            paint.setStrokeCap(Paint.Cap.ROUND);

        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(donutThickness);
        paint.setAntiAlias(true);

//        val invertedColors = donutColors.reversed(); // Degrees list come inverted
        List<Integer> invertedColors = new ArrayList<>();
        invertedColors.addAll(donutColors);
        Collections.reverse(invertedColors);

        for (int i = 0; i < degrees.size(); i++) {
            if (invertedColors.size() > i) {
                paint.setColor(invertedColors.get(i));
            }
            canvas.drawArc(
                    innerFrame.toRectF(),
                    defaultStartAngle,
                    degrees.get(i),
                    false,
                    paint
            );
        }
    }

    @Override
    public void drawBackground(Frame innerFrame) {
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(donutThickness);
        paint.setColor(donutBackgroundColor);
        paint.setAntiAlias(true);

        float radius = (innerFrame.getBottom() - innerFrame.getTop()) / 2;
        canvas.drawCircle(
                innerFrame.getLeft() + radius,
                innerFrame.getTop() + radius,
                radius,
                paint
        );
    }

    @Override
    public void drawDebugFrame(Frame innerFrame) {
        canvas.drawRect(innerFrame.toRect(), paint);
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        this.canvas = canvas;
        renderer.draw();
    }

    public void show(List<Float> values) {
        final ViewTreeObserver observer = getViewTreeObserver();
        OneShotPreDrawListener.add(this, new Runnable() {
            @Override
            public void run() {
                try {
                    renderer.preDraw(getDonutChartConfiguration());
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        });
        renderer.render(values);
    }

    public void animate(List<Float> values) {
//        final ViewTreeObserver observer = getViewTreeObserver();
//        observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
//            @Override
//            public boolean onPreDraw() {
//                observer.removeOnPreDrawListener(this);
//                try {
//                    renderer.preDraw(getDonutChartConfiguration());
//                } catch (IllegalAccessException e) {
//                    e.printStackTrace();
//                }
//                return false;
//            }
//        });
        OneShotPreDrawListener.add(this, new Runnable() {
            @Override
            public void run() {
                try {
                    renderer.preDraw(getDonutChartConfiguration());
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        });
        renderer.anim(values, animation);
    }

    private void handleAttributes(TypedArray typedArray) {
        donutThickness =
                typedArray.getDimension(R.styleable.DonutChartAttrs_chart_donutThickness, donutThickness);
        donutBackgroundColor = typedArray.getColor(
                R.styleable.DonutChartAttrs_chart_donutBackgroundColor,
                donutBackgroundColor
        );
        donutRoundCorners =
                typedArray.getBoolean(R.styleable.DonutChartAttrs_chart_donutRoundCorners, donutRoundCorners);
        donutTotal = typedArray.getFloat(R.styleable.DonutChartAttrs_chart_donutTotal, donutTotal);
        typedArray.recycle();
    }

    private void handleEditMode() {
        if (isInEditMode()) {
            show(editModeSampleData);
        }
    }

    public ChartAnimation<DonutDataPoint> getChartAnimation() {
        return animation;
    }

    public List<Integer> getDonutColors() {
        return donutColors;
    }

    public void setDonutColors(List<Integer> donutColors) {
        this.donutColors = donutColors;
    }
}
